package luj.tool.jnproxyan.proxy.util.data.wrap;

import luj.ava.collection.map.MapX;
import luj.bean.api.BeanContext;
import luj.bean.api.bean.CustomBean;
import luj.data.api.Data;
import luj.data.api.field.TypeField;
import luj.data.api.field.TypeInField;
import luj.data.api.use.DataUse;
import luj.data.api.use.UseGroup;
import luj.tool.jnproxyan.proxy.util.data.load.internal.CacheKeyMaker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.toMap;

@Component
public class DataResultWrapper {

  @Deprecated
  public <T> T wrap(Data data) {
    throw new RuntimeException("wrap未实现");
  }

  @Deprecated
  public <T> T wrap(Data data, Class<T> targetType, UseGroup group, boolean isNew) {
    DataUse use = group.use(data, targetType);
    if (isNew) {
      use.markNew();
    }

    ResultProxy custom = new ResultProxy();
    custom._using = use;
    custom._data = data;

    //TODO: 追踪集合里的复杂对象

    CustomBean<T> bean = _lujbean.createCustom(targetType, custom);
    return bean.getValueInstance();
  }

  public DataResultWrapper inject(Data data, Map<String, Data> loadCache, UseGroup useGroup) {
    return inject(data, loadCache, useGroup, false);
  }

  public DataResultWrapper inject(Data data, Map<String, Data> loadCache, UseGroup useGroup, boolean isNew) {
    DataResultWrapper result = new DataResultWrapper();
    result._data = data;
    result._loadCache = loadCache;
    result._useGroup = useGroup;
    result._isNew = isNew;

    result._lujbean = _lujbean;
    result._keyMaker = _keyMaker;

    return result;
  }

  public <T> T wrap(Class<T> targetType) {
    return wrapImpl(_data, targetType, _isNew);
  }

  private <T> T wrapImpl(Data data, Class<T> targetType, boolean isNew) {
    DataUse use = _useGroup.use(data, targetType);
    if (isNew) {
      use.markNew();
    }

    data.getType().getFields().stream()
        .filter(f -> f.getType().isMap())
        .forEach(f -> wrapMap(f, data));

    ResultProxy custom = new ResultProxy();
    custom._using = use;
    custom._data = _data;

    CustomBean<T> bean = _lujbean.createCustom(targetType, custom);
    return bean.getValueInstance();
  }

  private <T> T wrapFromId(Number id, Class<T> targetType) {
    String key = _keyMaker.make(targetType, id.longValue());
    Data data = _loadCache.get(key);
    checkNotNull(data, key);

    return wrapImpl(data, targetType, false);
  }

  private void wrapMap(TypeField field, Data data) {
    TypeInField.Map fieldType = field.getType().asMapType();
    TypeInField valueType = fieldType.getValueType();
    if (valueType.isPrimitive()) {
      return;
    }

    Map<Comparable<?>, Object> oldMap = field.getValue(data);
    if (MapX.nonNull(oldMap).isEmpty()) {
      return;
    }

    Class<?> valueClass = valueType.asJavaClass();
    Map<Comparable<?>, Object> newMap = oldMap.entrySet().stream()
        .collect(toMap(Map.Entry::getKey, e -> wrapFromId((Number) e.getValue(), valueClass)));

    oldMap.putAll(newMap);
  }

  Data _data;
  Map<String, Data> _loadCache;

  UseGroup _useGroup;
  boolean _isNew;

  @Autowired
  BeanContext _lujbean;

  @Autowired
  CacheKeyMaker _keyMaker;
}
